package com.shujia

import scala.io.{BufferedSource, Source}

object Demo12List {
  def main(args: Array[String]): Unit = {
    /**
     * List列表：通常用于存储一组相同类型的元素
     * 在Scala中的List默认是immutable包下面的，不可变
     * 有序，元素不唯一
     *
     */
    // 创建List xs:Int* 可变参数
    val list1: List[Int] = List[Int](1, 2, 2, 2, 2, 3, 4, 5, 6)
    val list2: List[Int] = List(1, 2, 3, 4, 5, 6)

    // 基础操作
    // 自带的比较基础的方法
    println(list1.max)
    println(list1.min)
    println(list1.size)
    println(list1.length)
    println(list1.head)
    println(list1.tail) // 返回除第一个元素之外的所有元素
    println(list1.isEmpty)
    println(list1.nonEmpty)
    println(list1.reverse)
    println(list1.sum)
    println(list1.distinct)

    println(list1)

    // 增删改查
    // 由于List是不可变的，所以增删改操作都没有
    // 查
    println(list1.indexOf(6))
    println(list1(5))

    // 高级操作
    // map、flatMap、filter、groupBy、sortBy、sortWith、sorted、foreach

    /**
     * map方法：用于遍历每一个元素，有返回值
     * 需要接收一个函数f：同List中每个元素的类型一致 => 返回值类型自己定
     * 会将List中的每一个元素传给函数f处理完之后再返回
     * 传入一个元素 会 返回一个元素
     * 最终每一个元素处理完之后会一起构成新的List并返回
     *
     */
    def f(int: Int): Int = {
      int * 3
    }

    val list3: List[Int] = List(1, 2, 3, 4, 5, 6)
    // 将list3中每个元素扩大3倍
    println(list3.map(f))

    list3.map(f)
      .foreach(println)

    // 使用匿名函数实现
    list3
      .map(i => i * 3)
      .foreach(println)

    /**
     * foreach：同map一样，会对每个元素进行遍历，但是没有返回值
     * 需要接收一个函数f：同List中每个元素的类型一致 => 返回值类型必须是Unit（即无返回值）
     * 会将List中的每一个元素传给函数f处理完之后不会再返回了，通常直接就输出了
     */

    list3.foreach(println)

    /**
     * flatMap：同map、foreach一样，都会对每个元素进行处理，有返回值
     * 需要接收一个函数f：同List中每个元素的类型一致 => GenTraversableOnce
     * 返回值类型比较特殊，简单看就是flatMap要求函数f的返回值类型可以是Array数组、List列表、Set集合等一些数据容器
     * 最后flatMap会将返回的容器进行展开（扁平化处理）
     * 传入一条会返回多条数据
     */

    val list4: List[String] = List("Java,Scala,Python", "Hadoop,Hive,HBase", "Spark,Flink,MR")

    list4.flatMap(s => s.split(",")).foreach(println)

    /**
     * filter：用于过滤元素
     * 同样需要接收一个函数p：同List中每个元素的类型一致 => Boolean
     * 如果传入一个元素给函数p，返回的是true，则保留该元素
     * 如果传入一个元素给函数p，返回的是false，则过滤该元素
     */

    val list5: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
    // 将list5中的奇数过滤出来
    list5.filter(i => i % 2 == 1).foreach(println)
    list5.filter(i => i % 2 == 0).foreach(println)

    /**
     * groupBy：用于分组，后续可以进行统计
     * 类似SQL中的分组聚合
     * 同样需要接收一个函数f：同List中每个元素的类型一致 => K
     * K实际上类型不限也由自己指定，只不过需要从传入的元素中取一部分来作为分组条件
     */

    val bs: BufferedSource = Source
      .fromFile("scala/data/stu/students.txt")

    // 统计班级人数
    val stuList: List[String] = bs
      .getLines()
      .toList

    stuList
      .map(line => line.split(",")(4)) // 提取班级
      .groupBy(clazz => clazz)
      .map(kv => s"${kv._1},${kv._2.size}")
      .foreach(println)


    bs.close()

    // sort相关的操作
    val list6: List[Int] = List(9, 4, 2, 1, 8, 7, 5, 6)

    println(list6.sorted) // 默认从小到大进行排序 并返回一个新的List

    /**
     * sortBy：指定按照元素的某一部分进行排序同groupBy类似
     */
    list6.sortBy(i => i).foreach(println)

    val list7: List[(Int, Int)] = List((1, 7), (3, 5), (2, 6))

    // 按照第一个元素从小到大排序
    list7.sortBy(t2 => t2._1).foreach(println)
    // 按照第二个元素从小到大排序
    list7.sortBy(t2 => t2._2).foreach(println)

    /**
     * sortWith：指定一个规则进行排序，该规则同compareTo方法类似
     * 需要接收两个参数
     */

    list6.sortWith((i1, i2) => i1 < i2).foreach(println)

    // 基于学生数据，先按照班级升序排列，如果班级相同再按照年龄升序，如果年龄也相同，最后再按照id降序排列
    stuList
      .sortWith((stu1, stu2) => {
        // 提取班级
        val clazz01: String = stu1.split(",")(4)
        val clazz02: String = stu2.split(",")(4)

        // 提取班级
        val id01: String = stu1.split(",")(0)
        val id02: String = stu2.split(",")(0)

        // 提取年龄
        val age01: Int = stu1.split(",")(2).toInt
        val age02: Int = stu2.split(",")(2).toInt

        val flag: Boolean =
          if (clazz01 == clazz02) {
            if (age01 == age02) {
              id01 > id02
            } else {
              age01 < age02
            }
          } else {
            clazz01 < clazz02
          }
        flag
      })
      .foreach(println)


  }

}
